////////////////////////////////////////////////////////////////////////////////
//
//  FLYING KIWI BV
//  Copyright 2010 Flying Kiwi BV
//  All Rights Reserved.
//
//  NOTICE: Flying Kiwi permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////
package plantool.modules.workplaces {
	//--------------------------------------------------------------------------
	//  Imports
	//--------------------------------------------------------------------------
	import flash.events.Event;
	
	import caurina.transitions.Tweener;
	
	import kiwi.display.DisplayObject;
	import kiwi.utils.Validation;
	
	import plantool.core.CoreModule;
	import plantool.data.QueryErrorResult;
	import plantool.data.QueryResult;
	import plantool.events.DateEvent;
	import plantool.events.OrderrowEvent;
	import plantool.events.UserEvent;
	import plantool.events.UserHourEvent;
	import plantool.events.WorkplaceEvent;
	import plantool.helpers.ClassHelper;
	import plantool.modules.orders.data.Orderplanning;
	import plantool.modules.users.Users;
	import plantool.modules.users.data.User;
	import plantool.modules.users.data.UserHourData;
	import plantool.modules.users.data.UserHourTypes;
	import plantool.modules.workplaces.data.Workplace;
	import plantool.modules.workplaces.display.IWorkplaceDay;
	import plantool.modules.workplaces.display.IWorkplaceOrderrow;
	import plantool.modules.workplaces.display.IWorkplaceRow;
	import plantool.modules.workplaces.display.IWorkplaceUserHour;
	import plantool.modules.workplaces.display.WorkplaceDay;
	import plantool.modules.workplaces.display.WorkplaceOrderrow;
	import plantool.modules.workplaces.display.WorkplaceRow;
	import plantool.modules.workplaces.display.WorkplaceUserHour;
	import plantool.settings.Settings;
	
	//--------------------------------------------------------------------------
	//  [Metadata (Events, Style, Effects etc)]
	//--------------------------------------------------------------------------
	
	//--------------------------------------------------------------------------
	//  Class declaration
	//--------------------------------------------------------------------------
	public class Workplaces extends CoreModule {
		//--------------------------------------------------------------------------
		//Class constants
		//--------------------------------------------------------------------------
		
		//--------------------------------------------------------------------------
		//Class variables
		//--------------------------------------------------------------------------
		private static var _instance:Workplaces						= null;
		private var _isInitialized:Boolean 							= false;
		private var _onCompleteHandler:Function						= null;
		private var _workplaceDayClass:Class						= WorkplaceDay;
		private var _workplaceDays:Vector.<IWorkplaceDay>			= new Vector.<IWorkplaceDay>();
		private var _workplaceUserHourClass:Class					= WorkplaceUserHour;
		private var _workplaceUserHours:Vector.<IWorkplaceUserHour>	= new Vector.<IWorkplaceUserHour>();
		private var _workplaceRowClass:Class						= WorkplaceRow;
		private var _workplaceRows:Vector.<IWorkplaceRow>			= new Vector.<IWorkplaceRow>();
		private var _workplaceOrderrowClass:Class					= WorkplaceOrderrow;
		private var _workplaceOrderrows:Vector.<IWorkplaceOrderrow>	= new Vector.<IWorkplaceOrderrow>();
		
		//--------------------------------------------------------------------------
		//Class properties
		//--------------------------------------------------------------------------
		public function get workplaceDayClass():Class { return _workplaceDayClass; }
		public function set workplaceDayClass(value:Class):void {
			//validate class
			if (Validation.isSet(value) && ClassHelper.validateClass(value, IWorkplaceDay)) {
				_workplaceDayClass = value;
			}
		}
		
		public function get workplaceUserHourClass():Class { return _workplaceUserHourClass; }
		public function set workplaceUserHourClass(value:Class):void {
			//validate class
			if (Validation.isSet(value) && ClassHelper.validateClass(value, IWorkplaceUserHour)) {
				_workplaceUserHourClass = value;
			}
		}
		
		public function get workplaceRowClass():Class { return _workplaceRowClass; }
		public function set workplaceRowClass(value:Class):void {
			//validate class
			if (Validation.isSet(value) && ClassHelper.validateClass(value, IWorkplaceRow)) {
				_workplaceRowClass = value;
			}
		}
		
		public function get workplaceOrderrowClass():Class { return _workplaceOrderrowClass; }
		public function set workplaceOrderrowClass(value:Class):void {
			//validate class
			if (Validation.isSet(value) && ClassHelper.validateClass(value, IWorkplaceOrderrow)) {
				_workplaceOrderrowClass = value;
			}
		}
		
		//--------------------------------------------------------------------------
		//Constructor and Destructor
		//--------------------------------------------------------------------------
		public function Workplaces(pvt:PrivateConstructorClass) {
			super();
		}
		
		//--------------------------------------------------------------------------
		//Class methods
		//--------------------------------------------------------------------------
		public static function getInstance():Workplaces {
			//check if instance exists, if not create one
			if (!Validation.isSet(_instance)) {
				_instance = new Workplaces(new PrivateConstructorClass());
			}
			
			//return instance
			return _instance;
		}
		
		private function createDaysForWorkplace(workplace:Workplace):void {
			//get userrow
			var row:IWorkplaceRow = getWorkplaceRow(workplace);
			
			//calculate how many days we can show on the screen
			var dateList:Vector.<Date> = dateApi.getVisibleDates();
			for (var i:uint = 0; i < dateList.length; i++) {
				//create day
				var day:IWorkplaceDay	= new workplaceDayClass();
				day.workplace			= workplace;
				day.date				= new Date(dateList[i]);
				
				//store userday
				_workplaceDays.push(day);
				
				//load user hours
				var from:Date 	= new Date(dateList[i]);
				var till:Date 	= new Date(dateList[i]);
				till.hours		+= dateApi.hoursPerDay;
				workplaceApi.getWorkplaceHours(row.workplace.id, from, till, onComplete_getUserHours, onError_query);
				
				//add day to userrow
				row.addDay(day);
			}
		}
		
		private function getDayByData(workplaceid:uint, from:Date, till:Date):Vector.<IWorkplaceDay> {
			//create filter function to filter results
			var filter:Function = function(item:IWorkplaceDay, idx:uint, list:Vector.<IWorkplaceDay>):Boolean {
				var dayEndDate:Date = new Date(item.date);
				dayEndDate.hours	+= dateApi.hoursPerDay;
				return (item.workplace.id == workplaceid && from.valueOf() >= item.date.valueOf() && till.valueOf() <= dayEndDate.valueOf());
			}
			
			//return list of hours
			return _workplaceDays.filter(filter);
		}
		
		private function getHoursByData(userid:uint, from:Date, till:Date):Vector.<IWorkplaceUserHour> {
			//create filter function to filter results
			var filter:Function = function(item:IWorkplaceUserHour, idx:uint, list:Vector.<IWorkplaceUserHour>):Boolean {
				return (item.user.id == userid && item.date.valueOf() >= from.valueOf() && item.date.valueOf() < till.valueOf());
			}
			
			//return list of hours
			return _workplaceUserHours.filter(filter);
		}
		
		private function initialized():void {
			//set initialized flag
			_isInitialized = true;
			
			//make this module visible
			Tweener.addTween(this, { _autoAlpha:1 });
			
			//database intact, call onCompleteHandler
			if (Validation.isSet(_onCompleteHandler)) {
				_onCompleteHandler();
			}
		}
		
		private function onChange_row(e:Event):void {
			var y:uint = 0;
			for (var i:uint = 0; i < _workplaceRows.length; i++) {
				//position row
				(_workplaceRows[i] as DisplayObject).y = y;
				
				//update y for next row
				y += (_workplaceRows[i] as DisplayObject).height;
			}
		}
		
		private function onComplete_getUserHours(result:QueryResult):void {
			//validate userdata as UserDay
			if (Validation.isSet(result.data) && result.data.length > 0) {
				for (var i:uint = 0; i < result.data.length; i++) {
					//validate result
					if (result.data[i] is UserHourData) {
						//cast result
						var hourData:UserHourData = result.data[i] as UserHourData;
						
						//get days for result
						var days:Vector.<IWorkplaceDay> = getDayByData(hourData.related_id, hourData.fromDate, hourData.tillDate);
						
						//create hour
						var hour:IWorkplaceUserHour 	= new workplaceUserHourClass();
						hour.date						= hourData.fromDate;
						hour.user		 				= userApi.getUserById(hourData.user_id);
						hour.hourData					= hourData;
						
						//store hour
						_workplaceUserHours.push(hour);
						
						//add hour
						for (var j:uint = 0; j < days.length; j++) {
							hour.workplace	= days[j].workplace;
							days[j].addHour(hour);
						}
					}
				}
			}
		}

		private function onComplete_getOrders(result:QueryResult):void {
			//validate userdata as UserDay
			if (Validation.isSet(result.data) && result.data.length > 0) {
				for (var i:uint = 0; i < result.data.length; i++) {
					//validate result
					if (result.data[i] is Orderplanning) {
						//cast result
						var orderPlanning:Orderplanning = result.data[i] as Orderplanning;
						
						//get days for result
						var sdate:Date					= databaseManager.createDateFromTimestamp(orderPlanning.date);
						var edate:Date					= new Date(sdate);
						edate.hours						+= dateApi.daysPerWeek;
						var days:Vector.<IWorkplaceDay> = getDayByData(orderPlanning.related_id, sdate, edate);
						
						//create order
						var order:IWorkplaceOrderrow 	= new workplaceOrderrowClass();
						order.orderData					= orderPlanning;
							
						//store hour
						_workplaceOrderrows.push(order);
						
						//add hour
						for (var j:uint = 0; j < days.length; j++) {
							order.workplace	= days[j].workplace;
							days[j].addPlannedOrder(order);
						}
					}
				}
			}
			
			//invaludate
			invalidate();
		}
		
		private function onComplete_getUsers(result:QueryResult):void {
			//load workplaces
			workplaceApi.getWorkplaces(onComplete_getWorkplaces, onError_query);
		}
		
		private function onComplete_getWorkplaces(result:QueryResult):void {
			//show workplaces
			if (Validation.isSet(result.data) && result.data.length > 0) {
				showWorkplaces(result.data);
			}
			
			//update row posisions
			onChange_row(null);
			
			//call initialized
			initialized();
		}
		
		private function onDate_change(e:DateEvent):void {
			//reset all hours
			while (_workplaceUserHours.length > 0) {
				//remove hour from vector
				var hour:IWorkplaceUserHour = _workplaceUserHours.pop();
				
				//dispose hour
				hour.dispose();
			}
			
			//change date on wordplace-days
			var dateList:Vector.<Date> = dateApi.getVisibleDates();
			for each (var row:IWorkplaceRow in _workplaceRows) {
				//get days
				var days:Vector.<IWorkplaceDay> = getWorkplaceDaysByID(row.workplace.id);
				
				//update all date's on the days
				for (var i:uint = 0; i < dateList.length; i++) {
					//set new date for day
					days[i].date = new Date(dateList[i]);
				}
			}
			
			//dispose all orderrows
			while (_workplaceOrderrows.length > 0) {
				//remove row from vector
				var orderrow:IWorkplaceOrderrow = _workplaceOrderrows.pop();
				
				//dispose the row
				orderrow.dispose();
			}
			
			//load new user hours
			var toDate:Date = new Date(dateList[dateList.length -1]);
			toDate.hours	+= dateApi.hoursPerDay;
			workplaceApi.getAllWorkplaceHours(dateList[0], toDate, onComplete_getUserHours, onError_query);
			
			//load the order data
			workplaceApi.getPlannedOrders(dateList[0], toDate, onComplete_getOrders, onError_query);
		}
		
		private function onError_query(result:QueryErrorResult):void {
			trace("Error in query: ", result.query);
			trace(" - ", result.message);
		}
		
		private function onResize_stage(e:Event):void {
			this.x	= parseInt(settings.getSetting(Settings.SETTING_GLOBAL_MARGIN).value);
			this.y = Users.getInstance().y + Users.getInstance().height + parseInt(settings.getSetting(Settings.SETTING_GLOBAL_MARGIN).value);
		}
		
		private function onOrderrowPlanning_updated(e:OrderrowEvent):void {
			//dispose old data
			for (var i:uint = 0; i < _workplaceOrderrows.length; i++) {
				if (_workplaceOrderrows[i].orderData.id == e.orderPlanning.id) {
					var row:WorkplaceOrderrow = _workplaceOrderrows[i];
					_workplaceOrderrows.splice(i, 1);
					
					row.dispose();
					row = null;
				}
			}
			
			//load the order data
			workplaceApi.getOrderPlanningByID(e.orderPlanning.id, onComplete_getOrders, onError_query);
		}
		
		private function onOrderrowPlanning_created(e:OrderrowEvent):void {
			//load the order data
			var endDate:Date 	= new Date(e.date);
			endDate.hours		+= dateApi.hoursPerDay;
			workplaceApi.getPlannedOrders(e.date, endDate, onComplete_getOrders, onError_query);
		}
		
		private function onWorkplaces_updated(e:WorkplaceEvent):void {
			//load workplaces
			workplaceApi.getWorkplaces(onComplete_getWorkplaces, onError_query);
		}
		
		private function onUserHours_updated(e:UserHourEvent):void {
			//get updated hours
			var hours:Vector.<IWorkplaceUserHour> = getHoursByData(e.userid, e.from, e.till);
			
			//reset all hours
			for (var i:uint = 0; i < hours.length; i++) {
				//remove hour from vector
				var idx:uint 		= _workplaceUserHours.indexOf(hours[i]);
				_workplaceUserHours.splice(idx, 1);
				
				//dispose hour
				hours[i].dispose();
			}
			
			//load new userhours
			userApi.getUserHours(e.userid, e.from, e.till, UserHourTypes.USER_WORKPLACE, onComplete_getUserHours, onError_query);
		}
		
		private function onUser_updated(e:UserEvent):void {
			//update user hours
			var user:User = userApi.getUserById(e.userid);
			if (Validation.isSet(user)) {
				//update hours
				var endDate:Date 	= dateApi.getStartDate();
				endDate.date		+= dateApi.daysPerWeek;
				var hours:Vector.<IWorkplaceUserHour> = getHoursByData(user.id, dateApi.getStartDate(), endDate);
				for each (var hour:IWorkplaceUserHour in hours) {
					hour.user = user;
				}
			}
		}
		
		private function showWorkplaces(workplaces:Array):void {
			//create a row for each user
			for (var i:uint = 0; i < workplaces.length; i++) {
				var row:IWorkplaceRow	= getWorkplaceRowByID(workplaces[i].id);
				if (Validation.isSet(row)) {
					//update row workplace
					row.workplace			= workplaces[i];
				} else {
					//create the userrow
					row			 	= new workplaceRowClass();
					row.workplace	= workplaces[i];
					
					//store user
					_workplaceRows.push(row);
					
					//create days for user
					createDaysForWorkplace(workplaces[i]);
					
					//add the row to the stage
					addWorkplace(row);
				}
			}
			
			//dispose all orderrows
			while (_workplaceOrderrows.length > 0) {
				//remove row from vector
				var orderrow:IWorkplaceOrderrow = _workplaceOrderrows.pop();
				
				//dispose the row
				orderrow.dispose();
			}
			
			//load the order data
			var dateList:Vector.<Date> = dateApi.getVisibleDates();
			var toDate:Date = new Date(dateList[dateList.length -1]);
			toDate.hours	+= dateApi.hoursPerDay;
			workplaceApi.getPlannedOrders(dateList[0], toDate, onComplete_getOrders, onError_query);
		}
		
		protected function addWorkplace(row:IWorkplaceRow):void {
			//position row
			(row as DisplayObject).addEventListener(Event.CHANGE		, onChange_row);
			
			//add rows
			addChild(row as DisplayObject);
		}
		
		protected function getWorkplaceDaysByID(id:uint):Vector.<IWorkplaceDay> {
			//filterfunction for userrows
			var filter:Function = function(item:IWorkplaceDay, idx:uint, list:Vector.<IWorkplaceDay>):Boolean {
				return item.workplace.id == id;
			}
			
			//filter the list
			var list:Vector.<IWorkplaceDay> = _workplaceDays.filter(filter);
			
			//sort the days by date
			var sort:Function = function(itemA:IWorkplaceDay, itemB:IWorkplaceDay):Number {
				if (itemA.date.valueOf() < itemB.date.valueOf()) return -1;
				if (itemA.date.valueOf() > itemB.date.valueOf()) return 1;
				return 0;
			}
			list.sort(sort);
			
			//check if result is found
			if (list.length > 0) {
				return list;
			}
			
			//nothing found
			return null;
		}
		
		protected function getWorkplaceRowByID(workplaceid:uint):IWorkplaceRow {
			//filterfunction for userrows
			var filter:Function = function(item:IWorkplaceRow, idx:uint, list:Vector.<IWorkplaceRow>):Boolean {
				return item.workplace.id == workplaceid;
			}
			
			//filter the list
			var list:Vector.<IWorkplaceRow> = _workplaceRows.filter(filter);
			//check if result is found
			if (list.length > 0) {
				return list[0];
			}
			
			//nothing found
			return null;
		}
		
		protected function getWorkplaceRow(workplace:Workplace):IWorkplaceRow {
			//filterfunction for userrows
			var filter:Function = function(item:IWorkplaceRow, idx:uint, list:Vector.<IWorkplaceRow>):Boolean {
				return item.workplace == workplace;
			}
			
			//filter the list
			var list:Vector.<IWorkplaceRow> = _workplaceRows.filter(filter);
			//check if result is found
			if (list.length > 0) {
				return list[0];
			}
			
			//nothing found
			return null;
		}
		
		protected override function init():void {
			//init events
			userApi.addEventListener(UserHourEvent.USER_HOUR_UPDATED		, onUserHours_updated);
			userApi.addEventListener(UserEvent.USER_UPDATED					, onUser_updated);
			workplaceApi.addEventListener(WorkplaceEvent.WORKPLACE_UPDATED	, onWorkplaces_updated);
			orderApi.addEventListener(OrderrowEvent.ORDERROWPLANNING_CREATED, onOrderrowPlanning_created);
			orderApi.addEventListener(OrderrowEvent.ORDERROWPLANNING_UPDATED, onOrderrowPlanning_updated);
			dateApi.addEventListener(DateEvent.DATE_CHANGED					, onDate_change);
			
			//listen to page invalidate to position this module
			base.stage.addEventListener(Event.RESIZE		, onResize_stage);
			
			//load users
			userApi.getUsers(onComplete_getUsers, onError_query);
			
		}
		
		public override function show(onCompleteHandler:Function=null):void {
			//check if we're initialized
			if (!_isInitialized) {
				//store oncompletehandler
				_onCompleteHandler	= onCompleteHandler;
			} else {
				//call oncompletehandler
				if (Validation.isSet(onCompleteHandler)) {
					onCompleteHandler();
				}
			}
		}
	}
}

//--------------------------------------------------------------------------
//END OF PACKAGE
//Define the PrivateConstructorClass here so an instance cannot be by anyone
//except for the class defined in the above package
//--------------------------------------------------------------------------
class PrivateConstructorClass {
	//--------------------------------------------------------------------------
	//Constructor and Destructor
	//--------------------------------------------------------------------------
	public function PrivateConstructorClass() {
	}
}